package environment.dungeons;


import static org.junit.Assert.*;
import org.junit.*;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import temperature.Temperature;

import environment.Direction;
import environment.Position;

public class ElementaryDungeonTest {

	static Level<Square> level;
	static Shaft<TeleportableSquare> shaft;
	static Square square1;
	static Square square2;
	static Square square3;
	static Square square4;
	static Position pos1;
	static Position pos2;
	static Position pos3;
	static Position pos4;
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		level = new Level<Square>();
		shaft = new Shaft<TeleportableSquare>(Direction.EAST);
		
		square1 = new NormalSquare();
		square2 = new TransparentSquare(Direction.CEILING);
		square3 = new Rock();
		square4 = new NormalSquare(Temperature.CELSIUS_1, BigDecimal.TEN, Direction.EAST, Direction.SOUTH);
	
		pos1 = new Position(5, 3, 0);
		pos2 = new Position(5, 0, 0);
		pos3 = new Position(5, 4, 0);
		pos4 = new Position(6, 3, 0);
	}

	@Test
	public void testGetElementarySubDungeons(){
		assertTrue((level.getElementarySubDungeons().contains(level)) && (level.getElementarySubDungeons().size() == 1));
		assertTrue((shaft.getElementarySubDungeons().contains(shaft)) && (shaft.getElementarySubDungeons().size() == 1));
	}
	
	@Test
	public void testAddingSquares(){
		//Adding + checking a NormalSquare
		level.addSquareAt(square1, pos1);
		assertTrue(level.hasAsSquare(square1));
		assertTrue(square1.isInUse());
		assertEquals(square1, level.getSquareAt(pos1));
		//Exception: already in use
		try {
			shaft.addSquareAt((NormalSquare)square1, pos2);
			assert false;
		} catch (IllegalArgumentException e) {
			assert true;
		}
		//Adding + checking a Transparant
		shaft.addSquareAt((TeleportableSquare)square2, pos2);
		assertTrue(shaft.hasAsSquare(square2));
		assertTrue(square2.isInUse());
		assertEquals(square2, shaft.getSquareAt(pos2));
		//Exception: not a TeleportableSquare
		try {
			shaft.addSquareAt((TeleportableSquare)square3, pos2);
			assert false;
		} catch (IllegalArgumentException e) {
			assert false;
		} catch (ClassCastException e) {
			assert true;
		}
		//Adding + checking a Rock
		level.addSquareAt(square3, pos3);
		assertTrue(level.hasAsSquare(square3));
		assertTrue(square3.isInUse());
		assertEquals(square3, level.getSquareAt(pos3));
		assertEquals(square1.getNeighbourIn(Direction.NORTH), square3);
		assertEquals(square3.getNeighbourIn(Direction.SOUTH), square1);
		assertEquals(square1.getTemperature(), square3.getTemperature());
		assertTrue(square3.getHumidity().compareTo(BigDecimal.ZERO) == 0);
		//Exception: ClosedBorder in the way of a Shaft
		try {
			shaft.addSquareAt((TeleportableSquare)square4, new Position(4, 0, 0));
			assert false;
		} catch (IllegalArgumentException e) {
			assert true;
		}
		//Adding + checking a NormalSquare
		level.addSquareAt(square4, pos4);
		assertTrue(level.hasAsSquare(square4));
		assertTrue(square4.isInUse());
		assertEquals(square4, level.getSquareAt(pos4));
		assertEquals(square1.getNeighbourIn(Direction.EAST), square4);
		assertEquals(square4.getNeighbourIn(Direction.WEST), square1);
		assertTrue(square1.getTemperature().compareTo( square4.getTemperature()) == 0);
		assertEquals(square1.getHumidity(), square4.getHumidity());
	}
	
	@Test
	public void testIterator(){
		//Level: 
		Iterator<Square> it = level.iterator();
		Set<Square> iteratedSquares = new HashSet<Square>();
		while(it.hasNext()){
			Square sq = it.next();
			iteratedSquares.add(sq);
			assertTrue(level.hasAsSquare(sq));
			assertTrue(sq.isInUse());
		}
		assertTrue(iteratedSquares.size() == level.getSquares().size());
		//Shaft: 
		Iterator<TeleportableSquare> it2 = shaft.iterator();
		Set<TeleportableSquare> iteratedSquares2 = new HashSet<TeleportableSquare>();
		while(it2.hasNext()){
			Square sq = it2.next();
			iteratedSquares2.add((TeleportableSquare)sq);
			assertTrue(shaft.hasAsSquare(sq));
			assertTrue(sq.isInUse());
		}
		assertTrue(iteratedSquares2.size() == shaft.getSquares().size());
	}
	
	@Test
	public void testRemovingSquares(){
		//Removing from level using the square
		level.removeAsSquare(square1);
		assertTrue(square1.isTerminated());
		assertFalse(level.hasAsSquare(square1));
		assertNull(level.getSquareAt(pos1));
		level.removeAsSquare(square4);
		assertTrue(square4.isTerminated());
		assertFalse(level.hasAsSquare(square4));
		assertNull(level.getSquareAt(pos4));
		//Removing from level using the position of the square
		level.removeSquareAt(pos3);
		assertTrue(square3.isTerminated());
		assertFalse(level.hasAsSquare(square3));
		assertNull(level.getSquareAt(pos3));
		//Removing from shaft using the square
		shaft.removeAsSquare((TeleportableSquare)square2);
		assertTrue(square2.isTerminated());
		assertFalse(shaft.hasAsSquare(square2));
		assertNull(shaft.getSquareAt(pos2));
	}
	
	@Test
	public void testTerminationDungeon(){
		//Level: 
		for(int i = 0; i< 11; i++)
			level.addSquareAt(new NormalSquare(), new Position(i, 10-i, 0));
		for(int i = 0; i< 11; i++)
			if(i != 8)
				level.addSquareAt(new Rock(), new Position(i, 2, 0));
		for(int i = 0; i< 11; i++)
			if(i != 5)
				level.addSquareAt(new TransparentSquare(Direction.EAST), new Position(i, 5, 0));
		Collection<Square> squaresSet= level.getSquares().values();
		level.terminate();
		for(Square levelSquare: squaresSet){
			assertTrue(! level.hasAsSquare(levelSquare));
			assertTrue(levelSquare.isTerminated());
		}
		assertEquals(level.getSquares().size(), 0);
		//Shaft: 
		for(int i = 1; i< 6; i++)
			shaft.addSquareAt(new NormalSquare(), new Position(i, 0, 0));
		for(int i = 6; i< 11; i++)
			shaft.addSquareAt(new TransparentSquare(Direction.SOUTH), new Position(i, 0, 0));
		Collection<TeleportableSquare> squaresSet2 = shaft.getSquares().values();
		shaft.terminate();
		for(TeleportableSquare levelSquare: squaresSet2){
			assertTrue(! shaft.hasAsSquare(levelSquare));
			assertTrue(levelSquare.isTerminated());
		}
		assertEquals(shaft.getSquares().size(), 0);
	}
}
